package tcptunnel

import (
	"context"
	"fmt"
	log "github.com/cihub/seelog"
	"github.com/songgao/water"
	"io"
	"net"
	"strconv"
	"strings"
	"time"
	"tunnel/authentication"
	"tunnel/common/config"
	"tunnel/pool"
	"tunnel/traffic"
	vtunnel "tunnel/tunnel"
)

type TCPClientV3 struct {
	IFace   *water.Interface
	Config  config.Config
	Context context.Context
	Cancel  context.CancelFunc
	Error   error
	//auth        *authentication.AuthClient
	authClient  *authentication.AuthClientV2
	address     string
	tunnelIndex int
	maxIndex    int
	size        int
	multiConn   *pool.MultiConn
	TxFP        *traffic.FlowProcessors
	RxFP        *traffic.FlowProcessors
}

//
// NewClientV3
// @Description: 持久化多连接接模型
// @return *TCPClientV3
//
func NewClientV3() *TCPClientV3 {
	size := 16
	return &TCPClientV3{
		IFace:       nil,
		Config:      config.Current,
		Error:       nil,
		size:        size,
		tunnelIndex: 0,
		maxIndex:    size - 1, // size -1
		multiConn:   nil,
	}
}

//
// Init
// @Description:
// @receiver C
// @return error
//
func (c *TCPClientV3) Init() error {
	//iface
	if c.IFace == nil {
		iface, err := vtunnel.CreateIFace(water.TUN)
		if err != nil {
			return err
		}
		err = vtunnel.SetupIFace(c.Config, iface)
		if err != nil {
			return err
		}
		c.IFace = iface
	}
	//preprocess address
	c.address = strings.Join([]string{c.Config.Global.Address, strconv.Itoa(c.Config.Global.Port)}, ":")
	//tx flow processor
	c.TxFP = traffic.NewFlowProcessor()
	TXFs := &traffic.FlowStatistics{Name: "tx"}
	c.TxFP.Register(TXFs)
	//go func() {
	//	for {
	//		time.Sleep(time.Second)
	//		fmt.Println("TX : tx_packet_speed=", TXFs.PacketSpeed, "p/s tx_flow_speed=", TXFs.FlowSpeed/1024/1024, "mb/s")
	//	}
	//}()
	//rx flow processor
	c.RxFP = traffic.NewFlowProcessor()
	RXFs := &traffic.FlowStatistics{Name: "rx"}
	go func() {
		for {
			time.Sleep(time.Second)
			fmt.Println("RX : rx_packet_speed=", TXFs.PacketSpeed, "p/s rx_flow_speed=", TXFs.FlowSpeed/1024/1024, "mb/s")
		}
	}()
	c.RxFP.Register(RXFs)
	return nil
}

//
// Stop
// @Description:
// @receiver C
//
func (c *TCPClientV3) Stop() {
	c.Cancel()
}

//
// SetErr
// @Description:
// @receiver c
// @param err
//
func (c *TCPClientV3) SetErr(err error) {
	c.Error = err
}

//
// Start
// @Description:
// @receiver C
// @return error
//
func (c *TCPClientV3) Start() error {
	//multi conn
	c.multiConn = pool.NewMultiConn("client")
	//context
	ctx, cancelFunc := context.WithCancel(context.Background())
	c.Context = ctx
	c.Cancel = cancelFunc
	//client
	client, err := authentication.NewClientV2(c.Config.Auth, &TCPAuthClientHandler{Client: c})
	if err != nil {
		return err
	}
	err = client.Login()
	if err != nil {
		return err
	}
	c.authClient = client
	for i := 0; i < c.size; i++ {
		//log.Info("create connection #", i+1)
		tcpAddr, err := net.ResolveTCPAddr("tcp4", c.address)
		//conn, err := net.Dial("tcp", c.address)
		conn, err := net.DialTCP("tcp", nil, tcpAddr)
		if err != nil {
			return err
		}
		//_ = conn.SetNoDelay(false)
		_ = conn.SetKeepAlive(true)
		//_ = conn.SetWriteBuffer(1024 * 1500)
		//_ = conn.SetReadBuffer(1024 * 1500)
		num := c.multiConn.Push(conn)
		go c.RXHandler(conn, num)
	}
	go c.TXHandler()
	select {
	case <-c.Context.Done():
		err := c.Error
		c.Error = nil
		return err
	}
}

//
// Logout
// @Description:
// @receiver c
//
func (c *TCPClientV3) Logout() {
	defer func() {
		if c.multiConn != nil {
			c.multiConn.Close()
		}
	}()
	err := c.authClient.Logout()
	if err != nil {
		_ = log.Warn("logout failed : ", err.Error())
		return
	}
	log.Info("client application logout")
}

//
// TXHandler
// @Description:
// @receiver C
//
func (c *TCPClientV3) TXHandler() {
	defer log.Info("[tx_handler] exit")
	packet := make([]byte, c.Config.Global.MTU)
	for {
		//interface -> tunnel
		n, err := c.IFace.Read(packet)
		if err != nil || n == 0 {
			continue
		}
		//TODO 流量处理
		if conn := c.multiConn.Get(); conn != nil {
			_, _ = conn.Write(c.TxFP.ProcessPositive(packet[:n]))
		}
		//log.Info("[traffic:tx][iface->tunnel] ", n, " bytes")
	}
}

//
// RXHandler
// @Description:
// @receiver C
//
func (c *TCPClientV3) RXHandler(conn *net.TCPConn, num int) {
	defer func() {
		if conn != nil {
			_ = conn.Close()
		}
		//log.Info("[rx][#", num, "] exit")
	}()
	buffer := make([]byte, c.Config.Global.MTU)
	for {
		n, err := conn.Read(buffer)
		if err != nil {
			if err == io.EOF {
			} else if strings.Contains(err.Error(), errClosedStr) {

			} else {
				log.Info("[rx][#", num, "] exit with error ", err.Error())
			}
			return
		}
		//TODO 流量处理
		n, _ = c.IFace.Write(c.RxFP.ProcessReverse(buffer[:n]))
		//log.Info("[traffic:rx][tunnel->iface] ", n, " bytes")
	}
}
